﻿using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Syndication;
using System.ServiceModel.Web;
using NCMIS.ObjectModel;
using NCMIS.ObjectModel.Rest;
using NCMIS.Produce;
using NCMIS.Provider;
using NCMIS.ServiceModel.Syndication;

namespace NCMIS.ServiceModel.Ra
{
    /// <summary>
    /// Provides the Discovery Services for the REST/Atom binding. The discovery services allows the search for query-able objects within the Repository.
    /// </summary>
    public partial class Service : IService
    {
        /// <summary>
        /// Executes a CMIS query statement against the contents of the Repository.
        /// </summary>
        /// <param name="repositoryId">the id of the repository.</param>
        /// <param name="statement">CMIS query statement to be executed.</param>
        /// <param name="searchAllVersions">If true, then the repository must include latest and non-latest versions of document objects in the query
        /// search scope. If false, then the repository must only include lastest versions of documents in the query search scope. (Optional, default: false)</param>
        /// <param name="includeAllowableActions">If true, then the the available actions for each object in the result set must be returned. (Optional, default: false)</param>
        /// <param name="includeRelationships">Value indicating what relationships in which the objects returned participate that must be returned, if any. (Optional, default: none)</param>
        /// <param name="renditionFilter">Renditions whose kind matches this filter must be returned. (Optional, default: cmis:none)</param>
        /// <param name="maxItems">The maximum number of items to return in the response. (optional, default: repository specific)</param>
        /// <param name="skipCount">The number of potential results to skip before returning any results. (optional, default: 0)</param>
        /// <returns>Objects matching query statement, formatted as Atom 1.0 Feed.</returns>
        [WebInvoke(Method = "POST", UriTemplate = CmisUriTemplate.Query)]
        public Atom10FeedFormatter Query(string repositoryId, string statement, bool searchAllVersions, bool includeAllowableActions, IncludeRelationships includeRelationships, string renditionFilter, string maxItems, string skipCount)
        {
            OutgoingWebResponseContext ctx = WebOperationContext.Current.OutgoingResponse;
            ctx.ContentType = CmisContentType.Feed;

            // Repository ID must be specified, otherwise throw an exception
            if (String.IsNullOrEmpty(repositoryId))
            {
                ctx.StatusCode = ServiceException.InvalidArgument;
                ctx.StatusDescription = String.Format("{0}: The parameters repositoryId and folderId must not be null or empty.",
                    ServiceException.InvalidArgument.ToString());
                return null;
            }

            // Execute the query and save the returned content in a CmisObjectList
            CmisObjectList cmisObjectList = DiscoveryProviderManager.Provider.Query(repositoryId, statement, searchAllVersions, includeAllowableActions, includeRelationships,
                renditionFilter, maxItems, skipCount);

            // Create a feed to hold the contents of the object list
            CmisSyndicationFeed feed = new CmisSyndicationFeed()
            {
                BaseUri = _baseUri,
                Id = "urn:uuid:base-types",
                RepositoryId = repositoryId,
                SelfLinkUri = new Uri(_baseUri, String.Format("{0}/types", repositoryId)),
                ServiceLinkUri = new Uri(_baseUri, String.Format("{0}/servicedoc", repositoryId)),
                Title = new TextSyndicationContent("Base Types"),
            };
            feed.AddEntries(cmisObjectList);
            feed.AddNumItems();

            // Format the feed as an Atom feed
            return new Atom10FeedFormatter(feed);
        }

        /// <summary>
        /// Gets a list of content changes. This service is intended to be used by search crawlers or other applications that need to efficiently
        /// understand what has changed in the repository.
        /// </summary>
        /// <param name="repositoryId">The id of the repository.</param>
        /// <param name="changeToken"> If specified, then the Repository must return the change event corresponding to the value of the specified change log token
        /// as the first result in the output.  If not specified, then the Repository must return the first change event recorded in the change log.</param>
        /// <param name="includeProperties">If true, then the Repository must include the updated property values for “updated” change events.  If false (default),
        /// then the Repository must not include the updated property values for "updated" change events. The single exception to this is that the objectId must always
        /// be included.</param>
        /// <param name="includePolicyIds">If true, then the Repository must include the IDs of Policies applied to the object referenced in each change event, if the change
        /// event modified the set of policies applied to the object. (Optional, default: false)</param>
        /// <param name="includeAcl">If true, then the repository must return the ACLs for each object in the result set. (Optional, default: false)</param>
        /// <param name="maxItems">The maximum number of items to return in the response. (optional, default: repository specific)</param>
        /// <returns></returns>
        [WebGet(UriTemplate = "/{repositoryId}/changes?changeToken={changeToken}&includeProperties={includeProperties}&includePolicyIds={includePolicyIds}&includeAcl={includeAcl}&maxItems={maxItems}")]
        public Atom10FeedFormatter GetContentChanges(string repositoryId, string changeToken, bool includeProperties, bool includePolicyIds, bool includeAcl, string maxItems)
        {
            string filter = RequestParameter.Filter;

            OutgoingWebResponseContext ctx = WebOperationContext.Current.OutgoingResponse;
            ctx.ContentType = CmisContentType.Feed;

            // Repository ID must be specified, otherwise throw an exception
            if (String.IsNullOrEmpty(repositoryId))
            {
                ctx.StatusCode = ServiceException.InvalidArgument;
                ctx.StatusDescription = String.Format("{0}: The parameters repositoryId and folderId must not be null or empty.",
                    ServiceException.InvalidArgument.ToString());
                return null;
            }

            // Saves the content changes in a tokenedCmisObjectList
            TokenedCmisObjectList tokenedCmisObjectList = DiscoveryProviderManager.Provider.GetContentChanges(repositoryId, changeToken, includeProperties,
                includePolicyIds, filter, includeAcl, maxItems);

            // Create a feed to hold the contents of the object list
            CmisSyndicationFeed feed = new CmisSyndicationFeed()
            {
                BaseUri = _baseUri,
                Id = "urn:uuid:changes",
                RepositoryId = repositoryId,
                //SelfLinkUri = new Uri(_baseUri, String.Format("{0}/types", repositoryId)),
                ServiceLinkUri = new Uri(_baseUri, String.Format("{0}/servicedoc", repositoryId)),
                Title = new TextSyndicationContent("Change Log"),
            };
            feed.AddEntries(tokenedCmisObjectList);
            feed.AddNumItems();

            // Format the feed as an Atom feed
            return new Atom10FeedFormatter(feed);
        }
    }
}
